﻿using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace VueShopApi.Common
{
    public class JwtHelper
    {
        private readonly IConfiguration _configuration;
        private readonly ILogger<JwtHelper> _logger;

        public JwtHelper(IConfiguration configuration, ILogger<JwtHelper> logger)
        {
            _configuration = configuration;
            _logger = logger;
        }

        /// <summary>
        /// 生成 token
        /// </summary>
        /// <returns></returns>
        public string IssueToken<T>(T principal) where T : class
        {
            var claims = new[]
            {
                new Claim(ClaimTypes.Name, "JWT"),
                new Claim("Principal", JsonConvert.SerializeObject(principal)),
            };
            var handler = new JwtSecurityTokenHandler();
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"]));
            var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var token = new JwtSecurityToken(
                claims: claims,
                expires: DateTime.Now.AddSeconds(double.Parse(_configuration["Jwt:ExpiresIn"])),
                signingCredentials: credentials);
            return handler.WriteToken(token);
        }

        /// <summary>
        /// 校验 token 
        /// </summary>
        /// <param name="token"></param>
        /// <param name="claimsPrincipal"></param>
        /// <returns></returns>
        public bool VerifyToken(string token, out ClaimsPrincipal claimsPrincipal)
        {
            var tokenStr = token.Replace("Bearer ", "");
            var handler = new JwtSecurityTokenHandler();
            if (handler.CanReadToken(tokenStr))
            {
                var tokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = false,           //是否验证 Issuer
                                                      // ValidIssuer = builder.Configuration["Jwt:Issuer"],
                    ValidateAudience = false,         //是否验证 Audience         
                                                      // ValidAudience = builder.Configuration["Jwt:Audience"],
                    ValidateIssuerSigningKey = true,  //是否验证 SecurityKey
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"])),
                    ValidateLifetime = true,          //是否验证 ExpirationTime
                    ClockSkew = TimeSpan.FromSeconds(double.Parse(_configuration["Jwt:ExpiresIn"])),
                    RequireExpirationTime = true
                };
                try
                {
                    claimsPrincipal = handler.ValidateToken(tokenStr, tokenValidationParameters, out var securityToken);
                    _logger.LogInformation("securityToken = {0}", securityToken);
                    return securityToken.ValidTo >= DateTime.Now;
                }
                catch (Exception ex)
                {
                    claimsPrincipal = null;
                    _logger.LogError(ex, ex.Message);
                    return false;
                }
            }

            claimsPrincipal = null;
            return false;
        }

        /// <summary>
        /// 解析 token
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public T ParseToken<T>(string token) where T : class
        {
            #region Simple, But unable to decrypt the principal

            //var tokenStr = token.Replace("Bearer ", "");
            //var handler = new JwtSecurityTokenHandler();
            //if (handler.CanReadToken(tokenStr))
            //{
            //    var payload = handler.ReadJwtToken(tokenStr).Payload;
            //    var claim = payload.Claims.FirstOrDefault(claim => claim.Type == "Principal")?.Value;
            //    return JsonConvert.DeserializeObject<T>(claim);
            //}
            //return null;

            #endregion

            if (VerifyToken(token, out ClaimsPrincipal claimsPrincipal))
            {
                var claim = claimsPrincipal.Claims.FirstOrDefault(claim => claim.Type == "Principal")?.Value;
                return JsonConvert.DeserializeObject<T>(claim);
            }

            return null;
        }
    }

    public class ManagerPrincipal
    {
        public ManagerPrincipal(int userId, int roleId)
        {
            UserId = userId;
            RoleId = roleId;
        }

        public int UserId { get; set; }
        public int RoleId { get; set; }
    }
}
